home *** CD-ROM | disk | FTP | other *** search
/ MacFormat 1996 September / macformat-041.iso / mac / Shareware City / Graphics / MacSPD / Sources / readobj.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-04-30  |  14.4 KB  |  544 lines  |  [TEXT/MMCC]

  1. /*
  2.  * readobj.c - Read polygons from a Wavefront OBJ file and display them.
  3.  * The file "view.dat" is processed for the camera view that will be used.
  4.  *
  5.  * Author:  Alexander Enzmann
  6.  *
  7.  * size_factor is ignored.
  8.  *
  9.  * This code was extracted from the Polyray raytracer.  Currently it will only
  10.  * read polygons, with or without associated vertex normals.  The u/v
  11.  * coordinates are read, but not used in this code.  Groups (including smoothing
  12.  * groups) are ignored.  All of the spline patch types are ignored.
  13.  *
  14.  */
  15.  
  16. #include <stdio.h>
  17. #include <math.h>
  18. #include <stdlib.h>     /* atoi */
  19. #include <string.h>     /* strcmp() */
  20. #include "def.h"
  21. #include "drv.h"       /* display_close() */
  22. #include "lib.h"
  23.  
  24.  
  25. /* These may be read from the command line */
  26. static int raytracer_format = OUTPUT_RT_DEFAULT;
  27. static int output_format    = OUTPUT_CURVES;
  28.  
  29.  
  30. #define MAXTRILINE 512
  31.  
  32. /* Data structure for a stack of vertices */
  33. typedef struct VecVerts_struct VecVerts;
  34. struct VecVerts_struct {
  35.    float V[4];
  36.    VecVerts *next;
  37.    };
  38.  
  39. /* Data structure for a stack of faces */
  40. typedef struct Face_struct Faces;
  41. struct Face_struct {
  42.    int vcount;
  43.    long *verts, *tverts, *nverts;
  44.    /* Texture *texture; */
  45.    Faces *next;
  46.    };
  47.  
  48. typedef struct triverts_struct triverts;
  49. struct triverts_struct {
  50.    COORD3 V[3], N[3], U[3];
  51.    /* Texture *texture; */
  52.    triverts *next;
  53.    };
  54.  
  55. /* Stack of raw triangle collections.  Each collection is associated
  56.    with a single texture name.  (currently unused) */
  57. typedef struct trivertstack_struct trivstack;
  58. struct trivertstack_struct {
  59.    /* Texture *texture; */  /* Texture to apply to all triangles */
  60.    triverts *verts;    /* List of triangles */
  61.    Faces *fstack;      /* Stack of face indices */
  62.    long tcount;        /* Number of triangles in this raw object */
  63.    int nflag;          /* Are there vertex normals? */
  64.    int uvflag;         /* Is there u/v information for the vertices? */
  65.    trivstack *next;    /* Next bag of triangles */
  66.    };
  67.  
  68. /* Structure to hold a smoothing group */
  69. typedef struct SmoothGroup_struct SmoothGroup;
  70. struct SmoothGroup_struct {
  71.    unsigned group_num;
  72.    Faces *faces;
  73.    SmoothGroup *next;
  74.    };
  75.  
  76. #define BETWEEN_OBJECTS  0
  77. #define READING_VERTICES 1
  78. #define READING_FACES    2
  79.  
  80. #define MAX_VERTICES_PER_FACE 16
  81.  
  82. static long vertex_count, vertex_texture_count, vertex_normal_count;
  83. static long face_count, object_count;
  84. /* static Texture *current_texture; */
  85.  
  86. static char rbuf[MAXTRILINE];
  87. static int rbuf_offset = 0;
  88. static int rbuf_length = 0;
  89.  
  90. static int
  91. skip_white_space(filep)
  92.    FILE *filep;
  93. {
  94.    for (;
  95.         (rbuf[rbuf_offset] == ' ' ||
  96.          rbuf[rbuf_offset] == '\t' ||
  97.          rbuf[rbuf_offset] == '\\') &&
  98.         rbuf_offset < rbuf_length;
  99.         rbuf_offset++)
  100.       if (rbuf[rbuf_offset] == '\\') {
  101.          /* Continuation character, get the next line */
  102.          if (fgets(rbuf, MAXTRILINE, filep) != NULL) {
  103.             rbuf_offset = 0;
  104.             rbuf_length = strlen(rbuf);
  105.             }
  106.          else {
  107.             rbuf[0] = '\0';
  108.             rbuf_offset = 0;
  109.             rbuf_length = 0;
  110.             return 0;
  111.             }
  112.          }
  113.       else {
  114.          /* White space, just ignore it */
  115.          }
  116.    return 1;
  117. }
  118.  
  119. static void
  120. skip_till_white_space(filep)
  121.    FILE *filep;
  122. {
  123.    for (;
  124.         rbuf[rbuf_offset] != ' ' &&
  125.         rbuf[rbuf_offset] != '\t' &&
  126.         rbuf_offset < rbuf_length;
  127.         rbuf_offset++)
  128.       ;
  129. }
  130.  
  131. static int
  132. end_of_line()
  133. {
  134.    if (rbuf_offset == rbuf_length ||
  135.        rbuf[rbuf_offset] == '\n' ||
  136.        rbuf[rbuf_offset] == '\0')
  137.       return 1;
  138.    else
  139.       return 0;
  140. }
  141.  
  142. static int
  143. read_vertex(filep, v, vt, vn)
  144.    FILE *filep;
  145.    long *v, *vt, *vn;
  146. {
  147.    float v0, vt0, vn0;
  148.  
  149.    skip_white_space(filep);
  150.    if (sscanf(&rbuf[rbuf_offset], "%g/%g/%g", &v0, &vt0, &vn0) == 3) {
  151.       *v  = (long)v0;
  152.       *vt = (long)vt0;
  153.       *vn = (long)vn0;
  154.       }
  155.    else if (sscanf(&rbuf[rbuf_offset], "%g//%g", &v0, &vn0) == 2) {
  156.       *v  = (long)v0;
  157.       *vt = (long)0L;
  158.       *vn = (long)vn0;
  159.       }
  160.    else if (sscanf(&rbuf[rbuf_offset], "%g/%g", &v0, &vt0) == 2) {
  161.       *v  = (long)v0;
  162.       *vt = (long)vt0;
  163.       *vn = (long)0L;
  164.       }
  165.    else if (sscanf(&rbuf[rbuf_offset], "%g", &v0) == 1) {
  166.       *v  = (long)v0;
  167.       *vt = 0L;
  168.       *vn = 0L;
  169.       }
  170.    else {
  171.       fprintf(stderr, "Bad vertex data\n");
  172.       exit(1);
  173.       }
  174.    skip_till_white_space(filep);
  175.    return 1;
  176. }
  177.  
  178. static Faces *
  179. read_face(filep)
  180.    FILE *filep;
  181. {
  182.    Faces *face;
  183.    int i, vcount, vtp_flag, vnp_flag;
  184.    long v[MAX_VERTICES_PER_FACE], *vp;
  185.    long vt[MAX_VERTICES_PER_FACE], *vtp;
  186.    long vn[MAX_VERTICES_PER_FACE], *vnp;
  187.  
  188.    vp = &v[0];
  189.    vtp = &vt[0];
  190.    vnp = &vn[0];
  191.    vtp_flag = 0;
  192.    vnp_flag = 0;
  193.    for (vcount=0;
  194.         vcount<MAX_VERTICES_PER_FACE && !end_of_line();
  195.         vcount++,vp++,vtp++,vnp++) {
  196.       read_vertex(filep, vp, vtp, vnp);
  197.       if (*vtp != 0)
  198.          vtp_flag = 1;
  199.       if (*vnp != 0)
  200.          vnp_flag = 1;
  201.       skip_white_space(filep);
  202.       }
  203.    if (vcount >= MAX_VERTICES_PER_FACE)
  204.       fprintf(stderr, "Too many vertices in a face");
  205.  
  206.    face = malloc(sizeof(Faces));
  207.    face->verts = malloc(vcount * sizeof(long));
  208.    if (vtp_flag)
  209.       face->tverts = malloc(vcount * sizeof(long));
  210.    else
  211.       face->tverts = NULL;
  212.    if (vnp_flag)
  213.       face->nverts = malloc(vcount * sizeof(long));
  214.    else
  215.       face->nverts = NULL;
  216.    face->vcount = vcount;
  217.    for (i=0;i<vcount;i++) {
  218.       if (v[i] > 0)
  219.          face->verts[i] = v[i] - 1;
  220.       else
  221.          face->verts[i] = vertex_count - vt[i];
  222.       if (vtp_flag) {
  223.          if (vt[i] > 0)
  224.             face->tverts[i] = vt[i] - 1;
  225.          else
  226.             face->tverts[i] = vertex_texture_count - vt[i];
  227.          }
  228.       if (vnp_flag) {
  229.          if (vn[i] > 0)
  230.             face->nverts[i] = vn[i] - 1;
  231.          else
  232.             face->nverts[i] = vertex_normal_count - vn[i];
  233.          }
  234.       }
  235.    face->next = NULL;
  236.    return face;
  237. }
  238.  
  239. static void
  240. make_triangles(vcount, normal_count, fstack,
  241.                vstack, nstack)
  242.    long vcount, normal_count;
  243.    Faces *fstack;
  244.    VecVerts *vstack, *nstack;
  245. {
  246.    COORD3 *V, *N;
  247.    long tcnt;
  248.    VecVerts *vtemp;
  249.    Faces *ftemp1, *ftemp2;
  250.  
  251.    /* Now we need to allocate space for the vertices and process
  252.       the face stacks into a set of triangles */
  253.    V = (COORD3 *)malloc(vcount * sizeof(COORD3));
  254.    if (normal_count > 0)
  255.       N = (COORD3 *)malloc(normal_count * sizeof(COORD3));
  256.    else
  257.       N = NULL;
  258.  
  259.    /* Copy the vertices into the V array */
  260.    for (tcnt=vcount-1;vstack!=NULL&&tcnt>=0;tcnt--) {
  261.       /* Copy this vertex into the array */
  262.       COPY_COORD3(V[tcnt], vstack->V)
  263.       /* Free up the space used for this vertex */
  264.       vtemp = vstack;
  265.       vstack = vstack->next;
  266.       free(vtemp);
  267.       }
  268.    if (tcnt != -1 || vstack != NULL) {
  269.       fprintf(stderr, "Didn't properly process .obj vertices");
  270.       exit(1);
  271.       }
  272.  
  273.    /* Copy the normals into the N array */
  274.    for (tcnt=normal_count-1;nstack!=NULL&&tcnt>=0;tcnt--) {
  275.       /* Copy this vertex into the array */
  276.       COPY_COORD3(N[tcnt], nstack->V)
  277.       /* Free up the space used for this vertex */
  278.       vtemp = nstack;
  279.       nstack = nstack->next;
  280.       free(vtemp);
  281.       }
  282.    if (tcnt != -1 || nstack != NULL) {
  283.       fprintf(stderr, "Didn't properly process .obj normals");
  284.       exit(1);
  285.       }
  286.  
  287.    /* Create triangles in the form we want them */
  288.    for (ftemp1=fstack,tcnt=0;ftemp1!=NULL;tcnt++) {
  289.       /* We need to turn the face into a set of triangles and
  290.          stuff each one onto the stack */
  291.       COORD3 *verts, *norms;
  292.       int j, npoints;
  293.  
  294.       /* Allocate temporary space to hold the polygon (yes,
  295.          I know this loop thrashes malloc(), but that's
  296.          just tough.  */
  297.       npoints = ftemp1->vcount;
  298.       verts = (COORD3 *)malloc(npoints * sizeof(COORD3));
  299.       if (ftemp1->nverts != NULL)
  300.          norms = (COORD3 *)malloc(npoints * sizeof(COORD3));
  301.       else
  302.          norms = NULL;
  303.  
  304.       /* Stuff the vertices of the polygon into the array
  305.          verts for subsequent chopping. */
  306.       for (j=0;j<npoints;j++) {
  307.          COPY_COORD3(verts[j], V[ftemp1->verts[j]])
  308.          if (norms != NULL)
  309.             COPY_COORD3(norms[j], N[ftemp1->verts[j]])
  310.          }
  311.       if (norms != NULL)
  312.          lib_output_polypatch(npoints, verts, norms);
  313.       else
  314.          lib_output_polygon(npoints, verts);
  315.  
  316.       /* Free the temporary polygon storage */
  317.       free(verts);
  318.       if (norms != NULL) free(norms);
  319.  
  320.       /* Dispose of the ones we just looked at */
  321.       ftemp2 = ftemp1;
  322.       ftemp1 = ftemp1->next;
  323.       free(ftemp2->verts);
  324.       if (ftemp2->tverts) free(ftemp2->tverts);
  325.       if (ftemp2->nverts) free(ftemp2->nverts);
  326.       free(ftemp2);
  327.       }
  328. }
  329.  
  330. static VecVerts *
  331. new_vecvert(x, y, z, w)
  332.    float x, y, z, w;
  333. {
  334.    VecVerts *vert;
  335.    vert = malloc(sizeof(VecVerts));
  336.    vert->V[0] = x;
  337.    vert->V[1] = y;
  338.    vert->V[2] = z;
  339.    vert->V[3] = w;
  340.    vert->next = NULL;
  341.    return vert;
  342. }
  343.  
  344. static int
  345. read_obj_faces(filep)
  346.    FILE *filep;
  347. {
  348.    char ctype[MAXTRILINE], tbuf1[MAXTRILINE], tbuf2[MAXTRILINE];
  349.    float v0, v1, v2, v3;
  350.    int icnt;
  351.    VecVerts *vstack, *nstack, *vtemp;
  352.    Faces *fstack, *ftemp1;
  353.  
  354.    fseek(filep, 0, SEEK_SET);
  355.  
  356.    vstack = NULL;
  357.    nstack = NULL;
  358.    fstack = NULL;
  359.    vertex_count = 0;
  360.    face_count = 0;
  361.    object_count = 0;
  362.    /* Read the entire file, processing triangles as we go. */
  363.    while(TRUE){
  364.       if (fgets(rbuf, MAXTRILINE, filep) == NULL)
  365.          break;
  366.       /* First read in the command for this line */
  367.       icnt = sscanf(rbuf, "%s", ctype);
  368.       rbuf_offset = strlen(ctype);
  369.       rbuf_length = strlen(rbuf);
  370.  
  371.       /* Looking for a statement like: "v x y z w" */
  372.       if (!strcmp(ctype, "v")) {
  373.          /* Read a vertex */
  374.          icnt = sscanf(rbuf, "%s %g %g %g %g", tbuf1, &v0, &v1, &v2, &v3);
  375.          if (icnt == 4 || icnt == 5) {
  376.             /* Valid vertex */
  377.             vtemp = new_vecvert(v0, v1, v2, (icnt == 4 ? 0.0 : v3));
  378.             vtemp->next = vstack;
  379.             vstack = vtemp;
  380.             vertex_count++;
  381.             }
  382.          else
  383.             fprintf(stderr, "Bad vertex");
  384.          continue;
  385.          }
  386.  
  387.       /* Looking for a statement like: "vn x y z" */
  388.       if (!strcmp(ctype, "vn")) {
  389.          /* Read a vertex */
  390.          icnt = sscanf(rbuf, "%s %g %g %g", tbuf1, &v0, &v1, &v2);
  391.          if (icnt == 4) {
  392.             /* Valid vertex */
  393.             vtemp = new_vecvert(v0, v1, v2, 0.0 );
  394.             vtemp->next = nstack;
  395.             nstack = vtemp;
  396.             vertex_normal_count++;
  397.             }
  398.          else
  399.             fprintf(stderr, "Bad normal");
  400.          continue;
  401.          }
  402.  
  403.       /* Looking for a statement like: "vt u v w" */
  404.       if (!strcmp(ctype, "vt")) {
  405.          /* Read a vertex */
  406.          icnt = sscanf(rbuf, "%s %g %g %g", tbuf1, &v0, &v1, &v2);
  407.          /* For now we are ignoring texture coordinates */
  408.          continue;
  409.          }
  410.  
  411.       /* Look for: "usemtl texture_name" */
  412.       if (!strcmp(ctype, "usemtl")) {
  413.          icnt = sscanf(rbuf, "%s %s", tbuf1, tbuf2);
  414.          if (icnt == 2) {
  415.             /* Got a texture name, do nothing for now */
  416.             }
  417.          else
  418.             fprintf(stderr, "Bad texture (usemtl) name");
  419.          continue;
  420.          }
  421.  
  422.       if (!strcmp(ctype, "f")) {
  423.          /* Read a face */
  424.          ftemp1 = read_face(filep);
  425.          if (ftemp1 != NULL) {
  426.             /* ftemp1->texture = current_texture; */
  427.             ftemp1->next = fstack;
  428.             fstack = ftemp1;
  429.             face_count++;
  430.             }
  431.          else
  432.             fprintf(stderr, "Bad face");
  433.          continue;
  434.          }
  435.       }
  436.  
  437.    /* Turn the contents of the face stack into triangle objects.  This
  438.       routine removes the memory associated with tristack. */
  439.    make_triangles(vertex_count, vertex_normal_count,
  440.                   fstack, vstack, nstack);
  441.  
  442.    return face_count;
  443. }
  444.  
  445. /* Read in the camera specifics: from, at, up, fov.  Aspect is hard coded
  446.     to 1.  */
  447. static void
  448. setup_view()
  449. {
  450.     char buffer[128];
  451.     COORD3 from, at, up;
  452.     double angle;
  453.     FILE *setup;
  454.  
  455.     /* output viewpoint */
  456.     if ((setup = fopen("view.dat", "r")) != NULL) {
  457.         if (fgets(buffer, 127, setup) &&
  458.              sscanf(buffer, "%lf %lf %lf",
  459.                       &from[X], &from[Y], &from[Z]) != 0 &&
  460.              fgets(buffer, 127, setup) &&
  461.              sscanf(buffer, "%lf %lf %lf",
  462.                       &at[X], &at[Y], &at[Z]) != 0 &&
  463.              fgets(buffer, 127, setup) &&
  464.              sscanf(buffer, "%lf %lf %lf",
  465.                       &up[X], &up[Y], &up[Z]) != 0 &&
  466.              fgets(buffer, 127, setup) &&
  467.              sscanf(buffer, "%lf", &angle)) {
  468.             lib_output_viewpoint(from, at, up, 45.0, 1.0, 1.0, 512, 512);
  469.         } else {
  470. #if defined(applec) || defined(THINK_C)
  471. #else
  472.             fprintf(stderr, "Invalid 'view.dat' file\n");
  473. #endif
  474.             exit(EXIT_FAIL);
  475.         }
  476.         fclose( setup );
  477.     } else {
  478.         SET_COORD3(from, 0, 10, -10);
  479.         SET_COORD3(at, 0, 0, 0);
  480.         SET_COORD3(up, 0, 0, 1);
  481.         lib_output_viewpoint(from, at, up, 45.0, 1.0, 1.0, 512, 512);
  482.     }
  483. }
  484.  
  485. /* Read in the camera view, then read in OBJ polygons, then display them. */
  486. int
  487. main(argc, argv)
  488.     int argc;
  489.     char *argv[];
  490. {
  491.     COORD3 back_color, dxf_color;
  492.     COORD4 light;
  493.     double lscale;
  494.     char file_name[64] ;
  495.     FILE *file;
  496.  
  497.     PLATFORM_INIT(SPD_READOBJ);
  498.  
  499.     /* Start by defining which raytracer we will be using */
  500.     if ( lib_read_get_opts( argc, argv,
  501.                         &raytracer_format, &output_format, file_name ) ) {
  502.         return EXIT_FAIL;
  503.     }
  504.     if ( lib_open( raytracer_format, "ReadOBJ.out" ) ) {
  505.         return EXIT_FAIL;
  506.     }
  507.  
  508.     file = fopen(file_name, "r");
  509.     if (file == NULL) {
  510.         fprintf(stderr, "Cannot open obj file: '%s'\n", file_name);
  511.         return EXIT_FAIL;
  512.     }
  513.  
  514.     lib_set_polygonalization(3, 3);
  515.  
  516.     /* output background color - dark blue */
  517.     /* NOTE: Do this BEFORE lib_output_viewpoint(), for display_init() */
  518.     SET_COORD3(back_color, 0.1, 0.0, 0.5);
  519.     lib_output_background_color(back_color);
  520.  
  521.     setup_view();
  522.  
  523.     /* output object color - light gray */
  524.     SET_COORD3(dxf_color, 0.8, 0.8, 0.8);
  525.     lib_output_color(NULL, dxf_color, 0.1, 0.8, 0.0, 0.2, 5.0, 0.0, 1.0);
  526.  
  527.     /* output light sources */
  528.     lscale = (raytracer_format == OUTPUT_NFF ||
  529.               raytracer_format == OUTPUT_RTRACE ? 1.0 : 1.0 / sqrt(2.0));
  530.     SET_COORD4(light, 40.0, 30.0, 20.0, lscale);
  531.     lib_output_light(light);
  532.     SET_COORD4(light, -40, -20, 10, lscale);
  533.     lib_output_light(light);
  534.  
  535.     read_obj_faces(file);
  536.  
  537.     fclose(file);
  538.  
  539.     lib_close();
  540.  
  541.     PLATFORM_SHUTDOWN();
  542.     return EXIT_SUCCESS;
  543. }
  544.